package ru.mirent.sentence.comparer;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import ru.mirent.sentence.comparer.SentenceComparer;

import java.util.List;
import java.util.stream.Stream;

public class SentenceComparerTest {
    private final SentenceComparer sentenceComparer = new SentenceComparer();

    @Test
    void normalizeSentenceTest() {
        String test = "Танимото, но, как4 и 455q ожидалось, они!!!!     показали____ плохие результаты. ";
        Assertions.assertEquals("танимото но как4 и 455q ожидалось они     показали плохие результаты ",
                sentenceComparer.normalizeSentence(test));
    }

    @Test
    void getTokensTest() {
        String test = "Танимото, но, как4 и 455q ожидалось, они!!!!     показали____ плохие результаты. ";
        System.out.println(sentenceComparer.getTokens(test, 3));

        Assertions.assertEquals(
                List.of("Танимото,", "но,", "как4", "455q", "ожидалось,", "они!!!!", "показали____", "плохие", "результаты."),
                sentenceComparer.getTokens(test, 3));
    }

    @Test
    void isNullOrWhiteSpaceTest() {
        Assertions.assertTrue(sentenceComparer.isNullOrWhiteSpace(null));
        Assertions.assertTrue(sentenceComparer.isNullOrWhiteSpace(""));
        Assertions.assertTrue(sentenceComparer.isNullOrWhiteSpace("  \t   "));
        Assertions.assertTrue(sentenceComparer.isNullOrWhiteSpace("  \n   "));

        Assertions.assertFalse(sentenceComparer.isNullOrWhiteSpace("ABCDE"));
    }

    @Test
    void isTokensFuzzyEqualTest() {
        Assertions.assertTrue(sentenceComparer.isTokensFuzzyEqual("ожидалосьплохие", "ожидалосьплохие"));
        Assertions.assertTrue(sentenceComparer.isTokensFuzzyEqual("ожидалосьп1о1и1", "ожидалосьплохие"));
        Assertions.assertFalse(sentenceComparer.isTokensFuzzyEqual("ожидал1с1п1о1и1", "ожидалосьплохие"));
    }

    @Test
    void getFuzzyEqualsTokensTest() {
        Assertions.assertEquals(
                List.of("ожидалось", "isNormalCharTest"),
                sentenceComparer.getFuzzyEqualsTokens(
                        List.of("isNorm12Char1234", "ожидалось", "isNormalCharTest", "результаты"),
                        List.of("isNormalCharTest", "ожидалось", "isNormalCharTest", "резуль12345")
                ));
    }

    @Test
    void isNormalCharTest() {
        Assertions.assertTrue(sentenceComparer.isNormalChar(' '));
        Assertions.assertTrue(sentenceComparer.isNormalChar('e'));
        Assertions.assertTrue(sentenceComparer.isNormalChar('4'));
        Assertions.assertFalse(sentenceComparer.isNormalChar('!'));
    }

    @ParameterizedTest
    @ArgumentsSource(TestDoubleData.class)
    void equalsSentenceTest(String first, String second, double coef) {
        Assertions.assertEquals(coef, sentenceComparer.calculateFuzzyEqualValue(first, second));
    }

    @ParameterizedTest
    @ArgumentsSource(TestBooleanData.class)
    void equalsSentenceTest(String first, String second, boolean expectedResult) {
        Assertions.assertEquals(expectedResult, sentenceComparer.isFuzzyEqual(first, second));
    }

    @Test
    void zeroMinWordLengthErrorTest() {
        // Вызов тестируемого метода
        RuntimeException runtimeException = Assertions.assertThrows(RuntimeException.class,
                () -> new SentenceComparer(0, 2, 0.25, 0.45));
        // Проверка соответствия сообщения об ошибке
        Assertions.assertEquals("Минимальная длинна слова не может быть менее или равной 0",
                runtimeException.getMessage());
    }

    @Test
    void zeroSubTokenLengthErrorTest() {
        // Вызов тестируемого метода
        RuntimeException runtimeException = Assertions.assertThrows(RuntimeException.class,
                () -> new SentenceComparer(3, 0, 0.25, 0.45));
        // Проверка соответствия сообщения об ошибке
        Assertions.assertEquals("Длинна подстроки не может быть менее или равной 0",
                runtimeException.getMessage());
    }

    @Test
    void negativeMinWordLengthErrorTest() {
        // Вызов тестируемого метода
        RuntimeException runtimeException = Assertions.assertThrows(RuntimeException.class,
                () -> new SentenceComparer(-7, 2, 0.25, 0.45));
        // Проверка соответствия сообщения об ошибке
        Assertions.assertEquals("Минимальная длинна слова не может быть менее или равной 0",
                runtimeException.getMessage());
    }

    @Test
    void negativeSubTokenLengthErrorTest() {
        // Вызов тестируемого метода
        RuntimeException runtimeException = Assertions.assertThrows(RuntimeException.class,
                () -> new SentenceComparer(3, -5, 0.25, 0.45));
        // Проверка соответствия сообщения об ошибке
        Assertions.assertEquals("Длинна подстроки не может быть менее или равной 0",
                runtimeException.getMessage());
    }

    @Test
    void positiveCreateObjectTest() {
        Assertions.assertDoesNotThrow(() -> new SentenceComparer());
        Assertions.assertDoesNotThrow(() -> new SentenceComparer(3, 2, 0.25, 0.45));
    }

    private static class TestDoubleData implements ArgumentsProvider {
        @Override
        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
            return Stream.of(
                    Arguments.of("Нечёткое сравнение строк: пойми меня, если сможешь",
                            "Нечёткое сравнение строк: пойми, если сможешь", 0.8571428571428571),
                    Arguments.of("Нечёткое сравнение строк: пойми меня, если сможешь",
                            "Большое несовпадение стравниваемых строк", 0.1),
                    Arguments.of("Одинаковые предложения",
                            "Одинаковые предложения", 1.0),
                    Arguments.of("Разные предложения",
                            "9879632 587412", 0.0),
                    Arguments.of("Второе предложение равно null",
                            null, 0.0),
                    Arguments.of(null,
                            "Перврое предложение равно null", 0.0)
            );
        }
    }

    private static class TestBooleanData implements ArgumentsProvider {
        @Override
        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
            return Stream.of(
                    Arguments.of("Нечёткое сравнение строк: пойми меня, если сможешь",
                            "Нечёткое сравнение строк: пойми, если сможешь", true),
                    Arguments.of("Нечёткое сравнение строк: пойми меня, если сможешь",
                            "Большое несовпадение стравниваемых строк", false),
                    Arguments.of("Одинаковые предложения",
                            "Одинаковые предложения", true),
                    Arguments.of("Разные предложения",
                            "9879632 587412", false),
                    Arguments.of("Второе предложение равно null",
                            null, false),
                    Arguments.of(null,
                            "Перврое предложение равно null", false)
            );
        }
    }
}
